/**
 * ...
 * @author Krzysztof Rozalski
 */

package hapi.display.sheet;

import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.DisplayObjectContainer;
import flash.display.Graphics;
import flash.display.PixelSnapping;
import flash.display.Sprite;
import flash.geom.Matrix;
import flash.geom.Point;
import flash.geom.Rectangle;

class BlitSpriteSheet implements SpriteSheet {

	var source:BitmapData;
	#if flash
	var canvas:BitmapData;
	var frameRectangles:IntHash<Rectangle>;
	#else
	var canvas:Graphics;
	var matrix:Matrix;
	#end
	var currentRect:Rectangle;
	public var currentFrame(default, null):Int;
	public var totalFrames(default, null):Int;
	public var width(default, null):Int;
	public var height(default, null):Int;
	public var scaleX(getScaleX, setScaleX):Float;
	public var scaleY(getScaleY, setScaleY):Float;
	public var x(getX, setX):Float;
	public var y(getY, setY):Float;
	var innerScaleX:Float;
	var innerScaleY:Float;
	public var visible(default, null):Bool;
	var originDest:Point;
	var rows:Int;
	var cols:Int;
	
	public function new(source:BitmapData, width:Int, height:Int, transparent=true, smoothing=false) {
		this.source = source;
		this.width = width;
		this.height = height;
		this.originDest = new Point(0, 0);
		this.visible = true;
		initFrames();
		#if !flash
			this.matrix = new Matrix(1, 0, 0, 1, 0, 0);
		#end
		currentFrame = 1;
	}
	
	public function goto(frame:Int) {
		if(frame <= totalFrames && frame >= 1 && this.currentFrame != frame) {
			this.currentFrame = frame;
			#if flash
				this.currentRect = frameRectangles.get(frame);
			#else
				var row = Math.ceil(frame/cols);
				var col = frame - (cols * (row - 1));
				this.matrix.tx = (1 - col) * width;
				this.matrix.ty = (1 - row) * height;
			#end
			render();
		}
	}
	
	function initFrames() {
		rows = Math.floor(source.height / height);
		cols = Math.floor(source.width / width);
		this.totalFrames = rows * cols;
		#if flash
		this.frameRectangles = new IntHash<Rectangle>(); 
		for ( r in 0...rows) {
			for ( c in 0...cols) {
				frameRectangles.set((r * cols) + (c + 1), new Rectangle(c * width, r * height, width, height)); 
			}
		}
		#end
	}
	
	inline function render() {
		#if flash
		canvas.copyPixels(source, currentRect, originDest);
		#else
		canvas.beginBitmapFill(source, matrix, true, false);
		canvas.drawRect(originDest.x, originDest.y, width, height); 
		canvas.endFill();
		#end
	}
	
	public function displayOn(root:DisplayObjectContainer, ?x = .0, ?y = .0):Void {
		#if flash
		if(Std.is(root, Bitmap)) { 
			drawOn(cast(root, Bitmap).bitmapData, x, y);
		}else {
			throw "BlitSpriteSheet can only be displayed on a Bitmap";
		}
		#else
		var gfx = Reflect.field(root, "graphics");
		if(gfx != null && Std.is(gfx, Graphics)) { 
			drawOn(gfx, x, y);
		}else {
			throw "BlitSpriteSheet can only be displayed on a Sprite, Shape or MovieClip";
		}
		#end
	}
	
	#if flash
	public function drawOn(canvas:BitmapData, ?x = .0, ?y = .0):Void {
	#else
	public function drawOn(canvas:Graphics, ?x = .0, ?y = .0):Void {
	#end
		this.canvas = canvas;
		this.x = x;
		this.y = y;
	}
	
	public function getCurrentFrame():Int {
		return currentFrame;
	}
	
	public function getFramesTotal():Int {
		return totalFrames;
	}
	
	public function show() {
		visible = true;
	}
	
	public function hide() {
		visible = false;
	}
	
	function getScaleX():Float {
		return innerScaleX;
	}
	
	function setScaleX(s:Float):Float {
		return innerScaleX = s;
	}
	
	function getScaleY():Float {
		return innerScaleY;
	}
	
	function setScaleY(s:Float):Float {
		return innerScaleY = s;
	}
	
	function getX():Float {
		return originDest.x;
	}
	
	function getY():Float {
		return originDest.y;
	}
	
	function setX(x:Float):Float {
		return this.originDest.x = x;
	}
	
	function setY(y:Float):Float {
		return this.originDest.y = y;
	}
	
}